/*M///////////////////////////////////////////////////////////////////////////////////////
//
//  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
//  By downloading, copying, installing or using the software you agree to this
license.
//  If you do not agree to this license, do not download, install,
//  copy or use the software.
//
//
//                          License Agreement
//                For Open Source Computer Vision Library
//
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
// Copyright (C) 2015, Itseez Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without
modification,
// are permitted provided that the following conditions are met:
//
//   * Redistribution's of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//
//   * Redistribution's in binary form must reproduce the above copyright
notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//
//   * The name of the copyright holders may not be used to endorse or promote
products
//     derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is"
and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are
disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any
direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/

#ifndef CMCOMMAND_LINE_PARSER_H
#define CMCOMMAND_LINE_PARSER_H

#include <iostream>
using namespace std;

enum struct Param
{
    INT          = 0,
    BOOLEAN      = 1,
    REAL         = 2,
    STRING       = 3,
    MAT          = 4,
    MAT_VECTOR   = 5,
    ALGORITHM    = 6,
    FLOAT        = 7,
    UNSIGNED_INT = 8,
    UINT64       = 9,
    UCHAR        = 11,
    SCALAR       = 12
};

template <typename _Tp, typename _EnumTp = void> struct ParamType
{
};

template <> struct ParamType<bool>
{
    typedef bool const_param_type;
    typedef bool member_type;

    static const Param type = Param::BOOLEAN;
};

template <> struct ParamType<int>
{
    typedef int const_param_type;
    typedef int member_type;

    static const Param type = Param::INT;
};

template <> struct ParamType<double>
{
    typedef double const_param_type;
    typedef double member_type;

    static const Param type = Param::REAL;
};

template <> struct ParamType<std::string>
{
    typedef const string &const_param_type;
    typedef string        member_type;

    static const Param type = Param::STRING;
};

template <> struct ParamType<float>
{
    typedef float const_param_type;
    typedef float member_type;

    static const Param type = Param::FLOAT;
};

template <> struct ParamType<unsigned>
{
    typedef unsigned const_param_type;
    typedef unsigned member_type;

    static const Param type = Param::UNSIGNED_INT;
};

template <> struct ParamType<unsigned long long>
{
    typedef unsigned long long const_param_type;
    typedef unsigned long long member_type;

    static const Param type = Param::UINT64;
};

template <> struct ParamType<unsigned char>
{
    typedef unsigned char const_param_type;
    typedef unsigned char member_type;

    static const Param type = Param::UCHAR;
};

template <typename _Tp>
struct ParamType<_Tp, typename std::enable_if<std::is_enum<_Tp>::value>::type>
{
    typedef typename std::underlying_type<_Tp>::type const_param_type;
    typedef typename std::underlying_type<_Tp>::type member_type;

    static const Param type = Param::INT;
};

/** @brief Designed for command line parsing

The sample below demonstrates how to use CommandLineParser:
@code
    CommandLineParser parser(argc, argv, keys);
    parser.about("Application name v1.0.0");

    if (parser.has("help"))
    {
        parser.printMessage();
        return 0;
    }

    int N = parser.get<int>("N");
    double fps = parser.get<double>("fps");
    std::string path = parser.get<String>("path");

    use_time_stamp = parser.has("timestamp");

    std::string img1 = parser.get<String>(0);
    std::string img2 = parser.get<String>(1);

    int repeat = parser.get<int>(2);

    if (!parser.check())
    {
        parser.printErrors();
        return 0;
    }
@endcode

### Keys syntax

The keys parameter is a std::string containing several blocks, each one is
enclosed in curly braces and describes one argument. Each argument contains
three parts separated by the `|` symbol:

-# argument names is a space-separated list of option synonyms (to mark argument
as positional, prefix it with the `@` symbol)
-# default value will be used if the argument was not provided (can be empty)
-# help message (can be empty)

For example:

@code{.cpp}
    const std::string keys =
        "{help h usage ? |      | print this message   }"
        "{@image1        |      | image1 for compare   }"
        "{@image2        |<none>| image2 for compare   }"
        "{@repeat        |1     | number               }"
        "{path           |.     | path to file         }"
        "{fps            | -1.0 | fps for output video }"
        "{N count        |100   | count of objects     }"
        "{ts timestamp   |      | use time stamp       }"
        ;
}
@endcode

Note that there are no default values for `help` and `timestamp` so we can check
their presence using the `has()` method. Arguments with default values are
considered to be always present. Use the `get()` method in these cases to check
their actual value instead.

String keys like `get<String>("@image1")` return the empty std::string `""` by
default - even with an empty default value. Use the special `<none>` default
value to enforce that the returned std::string must not be empty. (like in
`get<String>("@image2")`)

### Usage

For the described keys:

@code{.sh}
    # Good call (3 positional parameters: image1, image2 and repeat; N is 200,
ts is true) $ ./app -N=200 1.png 2.jpg 19 -ts

    # Bad call
    $ ./app -fps=aaa
    ERRORS:
    Parameter 'fps': can not convert: [aaa] to [double]
@endcode
 */
class CommandLineParser
{
  public:
    /** @brief Constructor

    Initializes command line parser object

    @param argc number of command line arguments (from main())
    @param argv array of command line arguments (from main())
    @param keys std::string describing acceptable command line parameters (see
    class description for syntax)
    */
    CommandLineParser(int argc, const char *const argv[],
                      const std::string &keys);

    /** @brief Copy constructor */
    CommandLineParser(const CommandLineParser &parser);

    /** @brief Assignment operator */
    CommandLineParser &operator=(const CommandLineParser &parser);

    /** @brief Destructor */
    ~CommandLineParser();

    /** @brief Returns application path

    This method returns the path to the executable from the command line
    (`argv[0]`).

    For example, if the application has been started with such a command:
    @code{.sh}
    $ ./bin/my-executable
    @endcode
    this method will return `./bin`.
    */
    std::string getPathToApplication() const;

    /** @brief Access arguments by name

    Returns argument converted to selected type. If the argument is not known or
    can not be converted to selected type, the error flag is set (can be checked
    with @ref check).

    For example, define:
    @code{.cpp}
    std::string keys = "{N count||}";
    @endcode

    Call:
    @code{.sh}
    $ ./my-app -N=20
    # or
    $ ./my-app --count=20
    @endcode

    Access:
    @code{.cpp}
    int N = parser.get<int>("N");
    @endcode

    @param name name of the argument
    @param space_delete remove spaces from the left and right of the std::string
    @tparam T the argument will be converted to this type if possible

    @note You can access positional arguments by their `@`-prefixed name:
    @code{.cpp}
    parser.get<String>("@image");
    @endcode
     */
    template <typename T>
    T get(const std::string &name, bool space_delete = true) const
    {
        T val = T();
        getByName(name, space_delete, ParamType<T>::type, (void *)&val);
        return val;
    }

    /** @brief Access positional arguments by index

    Returns argument converted to selected type. Indexes are counted from zero.

    For example, define:
    @code{.cpp}
    std::string keys = "{@arg1||}{@arg2||}"
    @endcode

    Call:
    @code{.sh}
    ./my-app abc qwe
    @endcode

    Access arguments:
    @code{.cpp}
    std::string val_1 = parser.get<String>(0); // returns "abc", arg1
    std::string val_2 = parser.get<String>(1); // returns "qwe", arg2
    @endcode

    @param index index of the argument
    @param space_delete remove spaces from the left and right of the std::string
    @tparam T the argument will be converted to this type if possible
     */
    template <typename T> T get(int index, bool space_delete = true) const
    {
        T val = T();
        getByIndex(index, space_delete, ParamType<T>::type, (void *)&val);
        return val;
    }

    /** @brief Check if field was provided in the command line

    @param name argument name to check
    */
    bool has(const std::string &name) const;

    /** @brief Check for parsing errors

    Returns false if error occurred while accessing the parameters (bad
    conversion, missing arguments, etc.). Call @ref printErrors to print error
    messages list.
     */
    bool check() const;

    /** @brief Set the about message

    The about message will be shown when @ref printMessage is called, right
    before arguments table.
     */
    void about(const std::string &message);

    /** @brief Print help message

    This method will print standard help message containing the about message
    and arguments description.

    @sa about
    */
    void printMessage() const;

    /** @brief Print list of errors occurred

    @sa check
    */
    void printErrors() const;

  protected:
    void getByName(const std::string &name, bool space_delete, Param type,
                   void *dst) const;
    void getByIndex(int index, bool space_delete, Param type, void *dst) const;

    struct Impl;
    Impl *impl;
};

#endif // CMCOMMAND_LINE_PARSER_H
